﻿#include "gtest/gtest.h"

#if defined(__linux__) || defined(__APPLE__)

#include "asycipc/linux_diagnostics.hpp"

#include <chrono>
#include <filesystem>
#include <random>
#include <stdexcept>
#include <string>
#include <sys/stat.h>
#include <unistd.h>

namespace fs = std::filesystem;

class LinuxFifoDiagnosticsTest : public ::testing::Test {
 protected:
  void SetUp() override {
    temp_dir_ = fs::temp_directory_path() /
                fs::path("asycipc_diag_" + std::to_string(::getpid()) + "_" +
                         std::to_string(RandomId()));
    fs::create_directories(temp_dir_);
  }

  void TearDown() override {
    std::error_code ec;
    fs::remove_all(temp_dir_, ec);
  }

  fs::path MakeFifo(const std::string& name) {
    fs::path path = temp_dir_ / name;
    if (::mkfifo(path.c_str(), 0600) != 0) {
      throw std::runtime_error("mkfifo failed");
    }
    return path;
  }

  void SetLastWrite(const fs::path& path, std::chrono::seconds age) {
    const auto now = fs::file_time_type::clock::now();
    fs::last_write_time(path, now - age);
  }

  static uint64_t RandomId() {
    static std::mt19937_64 rng{std::random_device{}()};
    return rng();
  }

  fs::path temp_dir_;
};

TEST_F(LinuxFifoDiagnosticsTest, EmptyDirectoryHasZeroStats) {
  auto stats = asycipc::linux_diagnostics::InspectFifoHealth(temp_dir_, std::chrono::seconds(300));
  EXPECT_EQ(stats.total_sessions, 0u);
  EXPECT_EQ(stats.active_sessions, 0u);
  EXPECT_EQ(stats.incomplete_sessions, 0u);
  EXPECT_EQ(stats.stale_sessions, 0u);
}

TEST_F(LinuxFifoDiagnosticsTest, HealthySessionCountedAsActive) {
  MakeFifo("session-alpha-in");
  MakeFifo("session-alpha-out");
  auto stats = asycipc::linux_diagnostics::InspectFifoHealth(temp_dir_, std::chrono::seconds(300));
  EXPECT_EQ(stats.total_sessions, 1u);
  EXPECT_EQ(stats.active_sessions, 1u);
  EXPECT_EQ(stats.incomplete_sessions, 0u);
  EXPECT_EQ(stats.stale_sessions, 0u);
}

TEST_F(LinuxFifoDiagnosticsTest, IncompleteSessionDetected) {
  MakeFifo("session-beta-in");
  SetLastWrite(temp_dir_ / "session-beta-in", std::chrono::seconds(600));
  auto stats = asycipc::linux_diagnostics::InspectFifoHealth(temp_dir_, std::chrono::seconds(300));
  EXPECT_EQ(stats.total_sessions, 1u);
  EXPECT_EQ(stats.active_sessions, 0u);
  EXPECT_EQ(stats.incomplete_sessions, 1u);
  EXPECT_EQ(stats.stale_sessions, 1u);
}

TEST_F(LinuxFifoDiagnosticsTest, StaleSessionDetected) {
  MakeFifo("session-gamma-in");
  MakeFifo("session-gamma-out");
  SetLastWrite(temp_dir_ / "session-gamma-in", std::chrono::seconds(600));
  SetLastWrite(temp_dir_ / "session-gamma-out", std::chrono::seconds(600));
  auto stats = asycipc::linux_diagnostics::InspectFifoHealth(temp_dir_, std::chrono::seconds(300));
  EXPECT_EQ(stats.total_sessions, 1u);
  EXPECT_EQ(stats.active_sessions, 0u);
  EXPECT_EQ(stats.incomplete_sessions, 0u);
  EXPECT_EQ(stats.stale_sessions, 1u);
}

TEST_F(LinuxFifoDiagnosticsTest, CollectDetailsReportsStatusAndAge) {
  MakeFifo("session-delta-in");
  MakeFifo("session-delta-out");
  SetLastWrite(temp_dir_ / "session-delta-in", std::chrono::seconds(120));
  SetLastWrite(temp_dir_ / "session-delta-out", std::chrono::seconds(120));

  auto details = asycipc::linux_diagnostics::CollectFifoSessions(temp_dir_, std::chrono::seconds(300));
  ASSERT_EQ(details.size(), 1u);
  const auto& info = details.front();
  EXPECT_EQ(info.session_id, "delta");
  EXPECT_TRUE(info.has_in);
  EXPECT_TRUE(info.has_out);
  EXPECT_FALSE(info.is_incomplete);
  EXPECT_FALSE(info.is_stale);
  EXPECT_NE(info.age, std::chrono::seconds::max());
}

#else

TEST(LinuxFifoDiagnosticsTest, SkipOnNonLinux) {
  GTEST_SKIP() << "Linux-only diagnostics";
}

#endif
